﻿#include "ItemObject.h"
#include "../Settings.h"
#include "../MirEnvir/Envir.h"
#include "../MirEnvir/Map.h"
#include "../MirDatabase/MovementInfo.h"
#include "../../Shared/ServerPackets.h"

using namespace Server::MirDatabase;
using namespace Server::MirEnvir;
namespace S = ServerPackets;

namespace Server::MirObjects
{

    ObjectType ItemObject::getRace() const
    {
        return ObjectType::Item;
    }

    std::string ItemObject::getName() const
    {
        return Item == nullptr ? "" : Item->Info->getFriendlyName();
    }

    void ItemObject::setName(const std::string &value)
    {
        throw NotSupportedException();
    }

    int ItemObject::getCurrentMapIndex() const
    {
        return CurrentMapIndex;
    }

    void ItemObject::setCurrentMapIndex(int value)
    {
        CurrentMapIndex = value;
    }

    Point *ItemObject::getCurrentLocation() const
    {
        return CurrentLocation;
    }

    void ItemObject::setCurrentLocation(Point *value)
    {
        CurrentLocation = value;
    }

    MirDirection ItemObject::getDirection() const
    {
        throw NotSupportedException();
    }

    void ItemObject::setDirection(MirDirection value)
    {
        throw NotSupportedException();
    }

    unsigned short ItemObject::getLevel() const
    {
        throw NotSupportedException();
    }

    void ItemObject::setLevel(unsigned short value)
    {
        throw NotSupportedException();
    }

    bool ItemObject::getBlocking() const
    {
        return false;
    }

    int ItemObject::getHealth() const
    {
        throw NotSupportedException();
    }

    int ItemObject::getMaxHealth() const
    {
        throw NotSupportedException();
    }

    ItemObject::ItemObject(MapObject *dropper, UserItem *item, bool DeathDrop)
    {
        if (DeathDrop) //player dropped it when he died: allow for time to run back and pickup his drops
        {
            ExpireTime = getEnvir()->getTime() + Settings::PlayerDiedItemTimeOut * Settings::Minute;
        }
        else
        {
            ExpireTime = getEnvir()->getTime() + Settings::ItemTimeOut * Settings::Minute;
        }

        Item = item;

        if (Item->isAdded())
        {
            NameColour = Color::Cyan;
        }
        else
        {
            if (item->Info->Grade == ItemGrade::None)
            {
                NameColour = Color::White;
            }
            if (item->Info->Grade == ItemGrade::Common)
            {
                NameColour = Color::White;
            }
            if (item->Info->Grade == ItemGrade::Rare)
            {
                NameColour = Color::DeepSkyBlue;
            }
            if (item->Info->Grade == ItemGrade::Legendary)
            {
                NameColour = Color::DarkOrange;
            }
            if (item->Info->Grade == ItemGrade::Mythical)
            {
                NameColour = Color::Plum;
            }
        }

        setCurrentMap(dropper->getCurrentMap());
        setCurrentLocation(dropper->getCurrentLocation());
    }

    ItemObject::ItemObject(MapObject *dropper, UserItem *item, Point *manualpoint)
    {
        ExpireTime = getEnvir()->getTime() + Settings::ItemTimeOut * Settings::Minute;

        Item = item;

        if (Item->isAdded())
        {
            NameColour = Color::Cyan;
        }
        else
        {
            if (item->Info->Grade == ItemGrade::None)
            {
                NameColour = Color::White;
            }
            if (item->Info->Grade == ItemGrade::Common)
            {
                NameColour = Color::White;
            }
            if (item->Info->Grade == ItemGrade::Rare)
            {
                NameColour = Color::DeepSkyBlue;
            }
            if (item->Info->Grade == ItemGrade::Legendary)
            {
                NameColour = Color::DarkOrange;
            }
            if (item->Info->Grade == ItemGrade::Mythical)
            {
                NameColour = Color::Plum;
            }
        }

        setCurrentMap(dropper->getCurrentMap());
        setCurrentLocation(manualpoint);
    }

    ItemObject::ItemObject(MapObject *dropper, unsigned int gold)
    {
        ExpireTime = getEnvir()->getTime() + Settings::ItemTimeOut * Settings::Minute;

        Gold = gold;

        setCurrentMap(dropper->getCurrentMap());
        setCurrentLocation(dropper->getCurrentLocation());
    }

    ItemObject::ItemObject(MapObject *dropper, unsigned int gold, Point *manuallocation)
    {
        ExpireTime = getEnvir()->getTime() + Settings::ItemTimeOut * Settings::Minute;

        Gold = gold;

        setCurrentMap(dropper->getCurrentMap());
        setCurrentLocation(manuallocation);
    }

    void ItemObject::Process()
    {
        if (getEnvir()->getTime() > ExpireTime)
        {
            getCurrentMap()->RemoveObject(this);
            Despawn();
            return;
        }

        if (Owner != nullptr && getEnvir()->getTime() > OwnerTime)
        {
            Owner = nullptr;
        }

        MapObject::Process();
    }

    void ItemObject::SetOperateTime()
    {
        long long time = getEnvir()->getTime() + 2000;

        if (OwnerTime < time && OwnerTime > getEnvir()->getTime())
        {
            time = OwnerTime;
        }

        if (ExpireTime < time && ExpireTime > getEnvir()->getTime())
        {
            time = ExpireTime;
        }

        if (PKPointTime < time && PKPointTime > getEnvir()->getTime())
        {
            time = PKPointTime;
        }

        if (LastHitTime < time && LastHitTime > getEnvir()->getTime())
        {
            time = LastHitTime;
        }

        if (EXPOwnerTime < time && EXPOwnerTime > getEnvir()->getTime())
        {
            time = EXPOwnerTime;
        }

        if (BrownTime < time && BrownTime > getEnvir()->getTime())
        {
            time = BrownTime;
        }

        for (int i = 0; i < ActionList.size(); i++)
        {
            if (ActionList[i]->Time >= time && ActionList[i]->Time > getEnvir()->getTime())
            {
                continue;
            }
            time = ActionList[i]->Time;
        }

        for (int i = 0; i < PoisonList.size(); i++)
        {
            if (PoisonList[i]->TickTime >= time && PoisonList[i]->TickTime > getEnvir()->getTime())
            {
                continue;
            }
            time = PoisonList[i]->TickTime;
        }

        for (int i = 0; i < Buffs.size(); i++)
        {
            if (Buffs[i]->ExpireTime >= time && Buffs[i]->ExpireTime > getEnvir()->getTime())
            {
                continue;
            }
            time = Buffs[i]->ExpireTime;
        }


        if (OperateTime <= getEnvir()->getTime() || time < OperateTime)
        {
            OperateTime = time;
        }
    }

    bool ItemObject::Drop(int distance)
    {
        if (getCurrentMap() == nullptr)
        {
            return false;
        }

        Cell *best = nullptr;
        int bestCount = 0;
        Point *bestLocation = Point::Empty;

        for (int d = 0; d <= distance; d++)
        {
            for (int y = getCurrentLocation()->Y - d; y <= getCurrentLocation()->Y + d; y++)
            {
                if (y < 0)
                {
                    continue;
                }
                if (y >= getCurrentMap()->Height)
                {
                    break;
                }

                for (int x = getCurrentLocation()->X - d; x <= getCurrentLocation()->X + d; x += std::abs(y - getCurrentLocation()->Y) == d ? 1 : d * 2)
                {
                    if (x < 0)
                    {
                        continue;
                    }
                    if (x >= getCurrentMap()->Width)
                    {
                        break;
                    }
                    if (!getCurrentMap()->ValidPoint(x, y))
                    {
                        continue;
                    }

                    bool movement = false;
                    for (int i = 0; i < getCurrentMap()->Info->Movements.size(); i++)
                    {
                        MovementInfo *info = getCurrentMap()->Info->Movements[i];
                        Point tempVar(x,y);
                        if (info->Source != &tempVar)
                        {
                            continue;
                        }
                        movement = true;
                        break;
                    }

                    if (movement)
                    {
                        continue;
                    }

                    Cell *cell = getCurrentMap()->GetCell(x, y);

                    if (cell->Objects.empty())
                    {
                        Point tempVar2(x, y);
                        setCurrentLocation(&tempVar2);
                        getCurrentMap()->AddObject(this);
                        Spawned();
                        return true;
                    }

                    int count = 0;
                    bool blocking = false;

                    for (int i = 0; i < cell->Objects.size(); i++)
                    {
                        MapObject *ob = cell->Objects[i];
                        if (ob->getBlocking())
                        {
                            blocking = true;
                            break;
                        }
                        if (ob->getRace() == ObjectType::Item)
                        {
                            count++;
                        }
                    }

                    if (blocking || count >= Settings::DropStackSize)
                    {
                        continue;
                    }

                    if (count == 0)
                    {
                        Point tempVar3(x, y);
                        setCurrentLocation(&tempVar3);
                        getCurrentMap()->AddObject(this);
                        Spawned();
                        return true;
                    }

                    if (best == nullptr || count < bestCount)
                    {
                        best = cell;
                        bestCount = count;
                        bestLocation = new Point(x, y);
                    }
                }
            }
        }

        if (best == nullptr)
        {

            delete bestLocation;
            return false;
        }

        setCurrentLocation(bestLocation);
        getCurrentMap()->AddObject(this);
        Spawned();

//C# TO C++ CONVERTER TODO TASK: A 'delete bestLocation' statement was not added since bestLocation was assigned to another object. Handle memory management manually.
        return true;
    }

    bool ItemObject::DragonDrop(int distance)
    {
        if (getCurrentMap() == nullptr)
        {
            return false;
        }

        Cell *best = nullptr;
        int bestCount = 0;
        Point *bestLocation = Point::Empty;

        for (int d = 0; d <= distance; d++)
        {
            for (int y = getCurrentLocation()->Y + 3; y <= getCurrentLocation()->Y + (d * 2); y++)
            {
                if (y < 0)
                {
                    continue;
                }
                if (y >= getCurrentMap()->Height)
                {
                    break;
                }

                for (int x = getCurrentLocation()->X - d; x <= getCurrentLocation()->X + d; x += std::abs(y - getCurrentLocation()->Y) == d ? 1 : d * 2)
                {
                    if (x < 0)
                    {
                        continue;
                    }
                    if (x >= getCurrentMap()->Width)
                    {
                        break;
                    }
                    if (!getCurrentMap()->ValidPoint(x, y))
                    {
                        continue;
                    }

                    bool movement = false;
                    for (int i = 0; i < getCurrentMap()->Info->Movements.size(); i++)
                    {
                        MovementInfo *info = getCurrentMap()->Info->Movements[i];
                        Point tempVar(x, y);
                        if (info->Source != &tempVar)
                        {
                            continue;
                        }
                        movement = true;
                        break;
                    }

                    if (movement)
                    {
                        continue;
                    }

                    Cell *cell = getCurrentMap()->GetCell(x, y);

                    if (cell->Objects.empty())
                    {
                        Point tempVar2(x, y);
                        setCurrentLocation(&tempVar2);
                        getCurrentMap()->AddObject(this);
                        Spawned();
                        return true;
                    }

                    int count = 0;
                    bool blocking = false;

                    for (int i = 0; i < cell->Objects.size(); i++)
                    {
                        MapObject *ob = cell->Objects[i];
                        if (ob->getBlocking())
                        {
                            blocking = true;
                            break;
                        }
                        if (ob->getRace() == ObjectType::Item)
                        {
                            count++;
                        }
                    }

                    if (blocking || count >= Settings::DropStackSize)
                    {
                        continue;
                    }

                    if (count == 0)
                    {
                        Point tempVar3(x, y);
                        setCurrentLocation(&tempVar3);
                        getCurrentMap()->AddObject(this);
                        Spawned();
                        return true;
                    }

                    if (best == nullptr || count < bestCount)
                    {
                        best = cell;
                        bestCount = count;
                        bestLocation = new Point(x, y);
                    }
                }
            }
        }

        if (best == nullptr)
        {

            delete bestLocation;
            return false;
        }

        setCurrentLocation(bestLocation);
        getCurrentMap()->AddObject(this);
        Spawned();

//C# TO C++ CONVERTER TODO TASK: A 'delete bestLocation' statement was not added since bestLocation was assigned to another object. Handle memory management manually.
        return true;
    }

    Packet *ItemObject::GetInfo()
    {
        if (Item != nullptr)
        {
            S::ObjectItem *tempVar = new S::ObjectItem();
            tempVar->ObjectID = ObjectID;
            tempVar->Name = Item->Count > 1 ? StringHelper::formatSimple("{0} ({1})", getName(), Item->Count) : getName();
            tempVar->NameColour = NameColour;
            tempVar->Location = getCurrentLocation();
            tempVar->Image = Item->getImage();

//C# TO C++ CONVERTER TODO TASK: A 'delete tempVar' statement was not added since tempVar was used in a 'return' or 'throw' statement.
            return tempVar;
        }

        S::ObjectGold *tempVar2 = new S::ObjectGold();
        tempVar2->ObjectID = ObjectID;
        tempVar2->Gold = Gold;
        tempVar2->Location = getCurrentLocation();

//C# TO C++ CONVERTER TODO TASK: A 'delete tempVar2' statement was not added since tempVar2 was used in a 'return' or 'throw' statement.
        return tempVar2;
    }

    void ItemObject::Process(DelayedAction *action)
    {
        throw NotSupportedException();
    }

    bool ItemObject::IsAttackTarget(PlayerObject *attacker)
    {
        throw NotSupportedException();
    }

    bool ItemObject::IsAttackTarget(MonsterObject *attacker)
    {
        throw NotSupportedException();
    }

    int ItemObject::Attacked(PlayerObject *attacker, int damage, DefenceType type, bool damageWeapon)
    {
        throw NotSupportedException();
    }

    int ItemObject::Attacked(MonsterObject *attacker, int damage, DefenceType type)
    {
        throw NotSupportedException();
    }

    int ItemObject::Struck(int damage, DefenceType type)
    {
        throw NotSupportedException();
    }

    void ItemObject::ApplyPoison(Poison *p, MapObject *Caster, bool NoResist, bool ignoreDefence)
    {
        throw NotSupportedException();
    }

    Buff *ItemObject::AddBuff(BuffType type, MapObject *owner, int duration, Stats *stat, bool visible, bool infinite, bool stackable, bool refreshStats, std::vector<int> &values)
    {
        throw NotSupportedException();
    }

    bool ItemObject::IsFriendlyTarget(PlayerObject *ally)
    {
        throw NotSupportedException();
    }

    bool ItemObject::IsFriendlyTarget(MonsterObject *ally)
    {
        throw NotSupportedException();
    }

    void ItemObject::Die()
    {
        throw NotSupportedException();
    }

    void ItemObject::SendHealth(PlayerObject *player)
    {
        throw NotSupportedException();
    }

    int ItemObject::Pushed(MapObject *pusher, MirDirection dir, int distance)
    {
        throw NotSupportedException();
    }

    void ItemObject::ReceiveChat(const std::string &text, ChatType type)
    {
        throw NotSupportedException();
    }
}
